#include "screen_rec\screencap.h"
#include "shared\dbg.h"
#include "avi\avifile.h"
namespace avlib{
#define CAPTURE_TIMER_ID  0x1000
#define BITSPERPIXEL		32


ScreenRecorder::ScreenRecorder(){
	memset(&m_region,0,sizeof(m_region));
	m_fps  = 0;
	mp_bits = NULL;
	m_bmp = 0;
	previewWnd = NULL;
	m_timer = 0;
	m_avi  = NULL;
}

ScreenRecorder::~ScreenRecorder(){
	
	stop();
	if(m_bmp){
		SelectObject(m_memdc,m_old);
		DeleteObject(m_bmp);
		m_bmp = 0;
	}

	if(m_memdc){
		DeleteDC(m_memdc);
		m_memdc = 0;
		m_old = 0;
	}

}

void ScreenRecorder::set_param(unsigned short fps,bool full_screen){
	m_fps = fps;
	if(full_screen){
		m_region.left = 0;
		m_region.top = 0;
		m_region.right =  GetSystemMetrics(SM_CXSCREEN);
		m_region.bottom = GetSystemMetrics(SM_CYSCREEN);
	}
}
void ScreenRecorder::set_region(LPRECT rect){
	m_region = *rect;
}



bool ScreenRecorder::init_bmp(){

	if(m_bmp){
		SelectObject(m_memdc,m_old);
		DeleteObject(m_bmp);
		m_bmp = 0;
	}

	if(m_memdc){
		DeleteDC(m_memdc);
		m_memdc = 0;
		m_old = 0;
	}

	//create mem dc and bitmap
	HWND h_desk  = GetDesktopWindow();
	HDC h_desk_dc = GetDC(h_desk);
	m_memdc = CreateCompatibleDC(h_desk_dc);
	ReleaseDC(h_desk,h_desk_dc);
		

	BITMAPINFO	bmpInfo;
	ZeroMemory(&bmpInfo,sizeof(BITMAPINFO));
	bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
	bmpInfo.bmiHeader.biBitCount = BITSPERPIXEL;
	bmpInfo.bmiHeader.biCompression = BI_RGB;
	bmpInfo.bmiHeader.biWidth = m_region.right - m_region.left;
	bmpInfo.bmiHeader.biHeight = (m_region.bottom - m_region.top); 
	bmpInfo.bmiHeader.biPlanes = 1;
	bmpInfo.bmiHeader.biSizeImage=abs(bmpInfo.bmiHeader.biHeight)*bmpInfo.bmiHeader.biWidth*bmpInfo.bmiHeader.biBitCount/8;


	log("create DIB size %d\n",bmpInfo.bmiHeader.biSizeImage);
	m_bmp = CreateDIBSection(m_memdc,&bmpInfo,DIB_RGB_COLORS, &mp_bits,NULL,0);
	if(m_bmp == NULL){
		assert(false);
		log("CreateDIBSection failed: %d",GetLastError());
		return false;
	}

	m_old = (HBITMAP)SelectObject(m_memdc,m_bmp);

	if(m_old == NULL){
		assert(false);
		log("SelectObject failed: %d",GetLastError());
		return false;
	}

	return true;
}




void WINAPI onTimerFunc(UINT wTimerID, UINT msg,DWORD dwUser,DWORD dwl,DWORD dw2){

	ScreenRecorder *recorder = (ScreenRecorder*)dwUser;

	recorder->blt();
	
}


inline bool point_in_rect(POINT pt, RECT rc){
	return (pt.x>=rc.left && pt.x< rc.right && pt.y >=rc.top && pt.y< rc.bottom);
}


void ScreenRecorder::blt(){

	DbgGuiLeak gui;
	
	//log("\nScreenRecorder::blt()\n");
	//EnterCriticalSection(&cs);
	//HWND h_desk = GetDesktopWindow();
	HDC h_desk_dc = GetDC(NULL);

	
	if(!h_desk_dc) {
		log("GetDC failed: %d\n",GetLastError());
		//assert(false);
		return ;
	}
	BOOL hr = BitBlt(m_memdc,0,0,m_region.right-m_region.left,m_region.bottom-m_region.top,h_desk_dc,m_region.left,m_region.top,SRCCOPY|CAPTUREBLT); 
	if(!hr){
			log("BitBlt error: %d",GetLastError());
			assert(false);
	}
	//ReleaseDC(h_desk,h_desk_dc);
	int res = ::ReleaseDC(NULL,h_desk_dc);
	
	//load cursor and draw cursoe
	CURSORINFO ci;
	ci.cbSize = sizeof(ci);
	if(::GetCursorInfo(&ci)){
		gui.report();

		//HICON hIcon = ::CopyIcon(ci.hCursor);
		HICON hIcon = ci.hCursor;
		if(hIcon != NULL)
		{
			ICONINFO ii;
			if(::GetIconInfo(hIcon,&ii))
			{
				gui.report();

				int x = ci.ptScreenPos.x - ((int)ii.xHotspot);
				int y = ci.ptScreenPos.y - ((int)ii.yHotspot);

				POINT pt;
				pt.x = x;
				pt.y = y;

				if(point_in_rect(pt,m_region)){
					if(::DrawIcon(m_memdc,x-m_region.left,y-m_region.top,hIcon)){
						log("DrawIcon error: %d",GetLastError());//ERROR_IO_PENDING
						//assert(false);
					}
					/*int cx = x-m_region.left;
					int cy = y-m_region.top;
					::SelectObject(m_memdc, GetStockObject(HOLLOW_BRUSH));
					::Ellipse(m_memdc, cx-10,cy-10,cx+10,cy+10);*/
				}
			}
			if(ii.hbmColor)::DeleteObject(ii.hbmColor);
			if(ii.hbmMask) ::DeleteObject(ii.hbmMask);
			//::DestroyIcon(hIcon);
		}else{
			log("CopyIcon error: %d",GetLastError());//ERROR_IO_PENDING
		}
	}

	gui.report();
	if(previewWnd){

		HDC hdc = GetWindowDC(previewWnd);
		RECT rc;
		if(hdc){
			GetWindowRect(previewWnd,&rc);
			//FillRect(hdc,&rc,(HBRUSH) (COLOR_WINDOW+1));
			if(!StretchBlt(hdc,0,0,rc.right-rc.left,rc.bottom-rc.top,m_memdc,0,0,m_region.right-m_region.left,m_region.bottom-m_region.top,SRCCOPY)){
				log("StretchBlt error: %d",GetLastError());
				assert(false);
			}
			ReleaseDC(previewWnd,hdc);
		}
	}
	gui.report();
	//GdiFlush();
	if(m_avi)
		m_avi->AppendVideoFrame(mp_bits,0);
	//LeaveCriticalSection(&cs);

	gui.report();

}

void ScreenRecorder::start(HWND hwnd,AviFile *avi){

	m_avi = avi;

	previewWnd = hwnd;
	assert(m_fps);
	assert((m_region.right-m_region.left > 0) && (m_region.bottom-m_region.top> 0));

	init_bmp();
	//InitializeCriticalSection(&cs);

	//m_timer=(UINT_PTR)SetTimer(NULL,CAPTURE_TIMER_ID,1000/m_fps,(TIMERPROC)MyTimerFunc);

	blt();
	m_timer = timeSetEvent(1000/m_fps,1000/m_fps,onTimerFunc,(DWORD)this,TIME_PERIODIC|TIME_CALLBACK_FUNCTION|TIME_KILL_SYNCHRONOUS);

}

void ScreenRecorder::stop(){
	//KillTimer(NULL,m_timer);
	if(m_timer != 0){
		//DeleteCriticalSection(&cs);

		MMRESULT res = timeKillEvent(m_timer);
		while(res != MMSYSERR_NOERROR){
			log("timeKillEvent error  %d",res);
			res = timeKillEvent(m_timer);

		}
		
		m_timer = 0;
	}

	m_avi = NULL;
}

/*HBITMAP ScreenRecorder::get_bmp(){
	return m_bmp;
}
void * ScreenRecorder::get_bits(){
	EnterCriticalSection(&cs);
	return mp_bits;
}

void ScreenRecorder::release_bits(){
	LeaveCriticalSection(&cs);
}*/

}